package juc;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Consumer;

/**
 * Condition使用1
 * 线程1：读数据，数据发生改变时，才可以读，，否则，阻塞
 * 线程2：写数据，数据已被使用时，才可以写，，否则，阻塞
 * @author ben
 * @date 2021-09-08 14:10:09 CST
 */
public class ConditionExample1 {

	public static Consumer<Object> cs = System.out::println;
	// 调试
	public static Consumer<Object> dbg = System.out::println;

	// 共享数据
	private static int shareData = 0;
	// 共享数据是否被使用
	private static boolean dataUsed = false;
	
	// 锁
//	private static final Lock lock = new ReentrantLock();
	// ReentrantLock类中有更多 Condition的方法
	private static final ReentrantLock lock = new ReentrantLock();
	// lock的Condition对象
	private static final Condition condition = lock.newCondition();
	
	/**
	 * 写数据
	 * @author ben
	 * @date 2021-09-08 14:14:54 CST
	 */
	public static void change() {
		String tname = Thread.currentThread().getName();
		// 未持有锁，抛出 IllegalMonitorStateException
//		dbg.accept(tname + ": start, Waiters=" + lock.hasWaiters(condition) + ", waitQL=" + lock.getWaitQueueLength(condition));
		dbg.accept(tname + ": start change()");
		lock.lock();
		dbg.accept(tname + ":1 Waiters=" + lock.hasWaiters(condition) + ", waitQL=" + lock.getWaitQueueLength(condition));
		try {
			while (!dataUsed) {
				dbg.accept(tname + ": change()...before await()");
				condition.await();
				dbg.accept(tname + ": change()...after await()");
			}
			dbg.accept(tname + ":2 Waiters=" + lock.hasWaiters(condition) + ", waitQL=" + lock.getWaitQueueLength(condition));
			
			// 修改数据，修改数据被使用标志为false
			// 休眠时间可调整
			TimeUnit.SECONDS.sleep(5);
			shareData++;
			dataUsed = false;
			cs.accept("change...shareData=" + shareData + ", dataUsed=" + dataUsed);
			
			// 通知并唤醒等待队列的其它线程
			condition.signal();
			dbg.accept(tname + ":3 Waiters=" + lock.hasWaiters(condition) + ", waitQL=" + lock.getWaitQueueLength(condition));
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			dbg.accept(tname + ":4 Waiters=" + lock.hasWaiters(condition) + ", waitQL=" + lock.getWaitQueueLength(condition));
			lock.unlock();
			dbg.accept(tname + ":end");
		}
	}
	
	/**
	 * 读数据
	 * @author ben
	 * @date 2021-09-08 14:15:04 CST
	 */
	public static void read() {
		String tname = Thread.currentThread().getName();
		// 未持有锁，抛出 IllegalMonitorStateException
//		dbg.accept(tname + ": start, Waiters=" + lock.hasWaiters(condition) + ", waitQL=" + lock.getWaitQueueLength(condition));
		dbg.accept(tname + ": start read()");
		lock.lock();
		dbg.accept(tname + ":1 Waiters=" + lock.hasWaiters(condition) + ", waitQL=" + lock.getWaitQueueLength(condition));
		try {
			// 数据已被读取，阻塞，等待修改为最新的
			while (dataUsed) {
				dbg.accept(tname + ": read()...before await()");
				condition.await();
				dbg.accept(tname + ": read()...after await()");
			}
			dbg.accept(tname + ":2 Waiters=" + lock.hasWaiters(condition) + ", waitQL=" + lock.getWaitQueueLength(condition));
			
			// 读取数据，修改数据被使用标志为true
			// 休眠时间可调整
			TimeUnit.SECONDS.sleep(5);
			dataUsed = true;
			cs.accept("read...shareData=" + shareData + ", dataUsed=" + dataUsed);
			
			// 通知并唤醒其它线程
			condition.signal();
			dbg.accept(tname + ":3 Waiters=" + lock.hasWaiters(condition) + ", waitQL=" + lock.getWaitQueueLength(condition));
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			dbg.accept(tname + ":4 Waiters=" + lock.hasWaiters(condition) + ", waitQL=" + lock.getWaitQueueLength(condition));
			lock.unlock();
			dbg.accept(tname + ":end");
		}
	}
	
	public static void main(String[] args) {
		cs.accept("主程序开始...");
		
		ExecutorService es = Executors.newFixedThreadPool(20);
		// 写数据线程
		es.execute(()->{
			while(true) {
				change();
			}
		});
		// 读数据线程
		es.execute(()->{
			while(true) {
				read();
			}
		});
		// 可以将上面的复制多个或循环，启动多个线程
		
		cs.accept("主程序结束...");
	}

}
